use crate::api::registry::ApiRegistry;
use crate::resp::HttpResponse;
use serde_json::Value;
use snafu::whatever;
use std::path::Path;

mod build;
mod check;
mod exec;
mod model;
mod registry;
mod schema;

pub struct Api {
    client: reqwest::Client,
    registry: ApiRegistry,
}

impl Api {
    pub async fn new(api_file: &Path, base_url: String, timeout_sec: u64) -> crate::Result<Self> {
        let registry = ApiRegistry::load(api_file, base_url).await?;
        let client = exec::client_with_default_timeout(timeout_sec)?;
        Ok(Self { client, registry })
    }

    pub async fn call(&self, name: &str, payload: Option<&Value>) -> crate::Result<HttpResponse> {
        let tpl = match self.registry.templates.get(name) {
            None => whatever!("Template not found"),
            Some(v) => v,
        };

        let body_opt: Option<Value> = match payload {
            Some(p) => Some(schema::materialize_payload(p, &tpl.fields)?),
            None => None,
        };

        let rb = build::build_request(&self.client, &tpl.spec, body_opt.as_ref())?;
        let resp = exec::execute(rb).await?;

        check::run_checks(&resp, &tpl.spec.checks)?;
        Ok(resp)
    }

    pub fn template_names(&self) -> Vec<&str> {
        self.registry.templates.keys().map(|k| k.as_str()).collect()
    }
}
